//
// Created by gj on 16-3-16.
//

#ifndef ALLUTILS_RC4OBJECT_H
#define ALLUTILS_RC4OBJECT_H

#include "rc4.h"
#include "../thread/Mutex.h"

class Rc4SBoxKey {
private:
    int32_t keySize;
    uint8_t *key;
public:
    Rc4SBoxKey() //
            : keySize(0), key(NULL) {
    }

    int32_t getKeySize() const {
        return keySize;
    }

    const uint8_t *getKey() const {
        return key;
    }

    void update(int32_t keySize, const uint8_t *key);

    bool isInvalid() const {
        return key == NULL || keySize <= 0;
    }

    void clear() {
        if (key != NULL) {
            delete key;
            key = NULL;
        }
        keySize = 0;
    }

};

/**
 * rc4 密钥处理模块
 * 主要负责rc4 密钥的获取和存储
 */
class CRc4KeyHandler {
public:
    /**
     * 获取rc4_key
     * @key     保证传入的key是清理过的，并且如果key获取失败，或者属于延时获取，需要将key置为NULL
     * @asyncObtain     要求必须同步保存，如果为true要求必须同步,反之
     * return true表示获取成功(异步请求发送成功也算成功)，如果同步成功时key.key == NULL，那么视为获取失败
     * 注意! 最好使用异步或者同线程非阻塞函数处理否则会出现递归,如果使用同步阻塞函数，则必须返回正确值
     */
    virtual bool obtainRc4Key(Rc4SBoxKey &key, bool syncObtain) = 0;

    /**
     * 获取本地缓存的key，策略同{obtainRc4Key}
     */
    virtual bool obtainCacheRc4Key(Rc4SBoxKey &key, bool syncObtain) = 0;

    /**
     * 将key保存到本地缓存，即{obtainCacheRc4Key}取出来的值
     * @asyncObtain     要求必须同步保存，如果为true要求必须同步,反之
     */
    virtual void saveRc4Key(Rc4SBoxKey &key, bool syncObtain) = 0;
};

typedef SharePointer<CRc4KeyHandler> Rc4KeyHandler;

/**
 * rc4 密钥配置模块
 */
class Rc4SBoxConfig {
public:
#define RC4_SBOX_NUM 256
    static Rc4SBoxConfig *_instance;

    /**
     * 在程度启动时初始化一次即可，或者需要替换全局单例时
     * 不支持多线程情况
     * init 会释放之前的单例，重新生成一个单例
     */
    static void init(Rc4KeyHandler &nativeRc4KeyHandler, Rc4KeyHandler &dynamicRc4KeyHandler);

    static Rc4SBoxConfig *getInstance() {
        return _instance;
    }

    Rc4SBoxConfig(Rc4KeyHandler &nativeRc4KeyHandler, Rc4KeyHandler &dynamicRc4KeyHandler)
            : nativeRc4KeyHandler(nativeRc4KeyHandler), dynamicRc4KeyHandler(dynamicRc4KeyHandler) {
        _init();
    }

    /**
     * 更新key
     * 与copySBox互斥，是线程安全的
     * 注意： 若发现key或者keySize无效，会重新调用disable
     */
    void updateKey(const uint8_t *key, int32_t keySize);

    /**
     * 获取当前sbox
     * 与updateKey互斥，是线程安全的
     * @sbox        要copy到的数组，大小必须为S_BOX_NUM
     * return 会返回sbox
     */
    void copySBox(bool &d, time_t &t, uint8_t *s_box) {
        AutoMutex autoMutex(mutex);
        _copy(d, t, s_box);
    }

    /**
     * 禁用私钥
     */
    void disable(bool &d, time_t &t, uint8_t *s_box);

    time_t getUpdateTime() {
        return keyUpdateTime;
    }

private:
    bool isDisable;
    Rc4SBoxKey rc4Key;
    time_t keyUpdateTime;
    uint8_t s_box[RC4_SBOX_NUM];
    uint8_t s_box_native[RC4_SBOX_NUM];

    CMutex mutex;

    Rc4KeyHandler nativeRc4KeyHandler;
    Rc4KeyHandler dynamicRc4KeyHandler;

    void _init();

    void _updateKeyForNative();

    void _copy(bool &d, time_t &t, uint8_t *s_box) const;
};

/**
 * 主要在一整个加解密生命周期中使用
 * 避免当一个异步请求用rc4密钥加密后，结果返回时，全局密钥改变，而返回值的加密密钥没有改变
 */
class Rc4Object {
public:

    Rc4Object() //
            : isDisable(false), keyUpdateTime(0) {
        update();
    }

/**
     * 更新密钥缓存
     * return 更新后是否可以立即使用
     */
    void update();

    /**
     * 开始rc4加/解密
     * @data    要加/解密的串
     * @size    串长度
     * return data
     */
    uint8_t *startRc4(uint8_t *data, int32_t size);

    bool isDisabled() const {
        return isDisable;
    }

private:
    bool isDisable;
    time_t keyUpdateTime;
    uint8_t s_box[RC4_SBOX_NUM];

    CMutex mutex;
};

// --------------------------------------------- unit test --------------------------------------------------
#ifdef FLAG_UNIT_TESTING

void rc4_unit_testing();

#endif

#endif //ALLUTILS_RC4_H
